/*
 * Copyright 2019 New Vector Ltd
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package im.vector.riotx.features.crypto.keysbackup.settings

import android.view.View
import com.airbnb.epoxy.TypedEpoxyController
import com.airbnb.mvrx.Async
import com.airbnb.mvrx.Fail
import com.airbnb.mvrx.Loading
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.Uninitialized
import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupState
import im.vector.matrix.android.internal.crypto.keysbackup.model.KeysBackupVersionTrust
import im.vector.riotx.R
import im.vector.riotx.core.epoxy.errorWithRetryItem
import im.vector.riotx.core.epoxy.loadingItem
import im.vector.riotx.core.resources.StringProvider
import im.vector.riotx.core.ui.list.GenericItem
import im.vector.riotx.core.ui.list.genericItem
import im.vector.riotx.features.settings.VectorPreferences
import java.util.UUID
import javax.inject.Inject

class KeysBackupSettingsRecyclerViewController @Inject constructor(private val stringProvider: StringProvider,
                                                                   private val vectorPreferences: VectorPreferences,
                                                                   private val session: Session) : TypedEpoxyController<KeysBackupSettingViewState>() {

    var listener: Listener? = null

    override fun buildModels(data: KeysBackupSettingViewState?) {
        if (data == null) {
            return
        }

        var isBackupAlreadySetup = false

        val keyBackupState = data.keysBackupState
        val keyVersionResult = data.keysBackupVersion

        when (keyBackupState) {
            KeysBackupState.Unknown                    -> {
                errorWithRetryItem {
                    id("summary")
                    text(stringProvider.getString(R.string.keys_backup_unable_to_get_keys_backup_data))
                    listener { listener?.loadKeysBackupState() }
                }

                // Nothing else to display
                return
            }
            KeysBackupState.CheckingBackUpOnHomeserver -> {
                loadingItem {
                    id("summary")
                    loadingText(stringProvider.getString(R.string.keys_backup_settings_checking_backup_state))
                }
            }
            KeysBackupState.Disabled                   -> {
                genericItem {
                    id("summary")
                    title(stringProvider.getString(R.string.keys_backup_settings_status_not_setup))
                    style(GenericItem.STYLE.BIG_TEXT)

                    if (data.keysBackupVersionTrust()?.usable == false) {
                        description(stringProvider.getString(R.string.keys_backup_settings_untrusted_backup))
                    }
                }

                isBackupAlreadySetup = false
            }
            KeysBackupState.WrongBackUpVersion,
            KeysBackupState.NotTrusted,
            KeysBackupState.Enabling                   -> {
                genericItem {
                    id("summary")
                    title(stringProvider.getString(R.string.keys_backup_settings_status_ko))
                    style(GenericItem.STYLE.BIG_TEXT)
                    if (data.keysBackupVersionTrust()?.usable == false) {
                        description(stringProvider.getString(R.string.keys_backup_settings_untrusted_backup))
                    } else {
                        description(keyBackupState.toString())
                    }
                    endIconResourceId(R.drawable.unit_test_ko)
                }

                isBackupAlreadySetup = true
            }
            KeysBackupState.ReadyToBackUp              -> {
                genericItem {
                    id("summary")
                    title(stringProvider.getString(R.string.keys_backup_settings_status_ok))
                    style(GenericItem.STYLE.BIG_TEXT)
                    if (data.keysBackupVersionTrust()?.usable == false) {
                        description(stringProvider.getString(R.string.keys_backup_settings_untrusted_backup))
                    } else {
                        description(stringProvider.getString(R.string.keys_backup_info_keys_all_backup_up))
                    }
                    endIconResourceId(R.drawable.unit_test_ok)
                }

                isBackupAlreadySetup = true
            }
            KeysBackupState.WillBackUp,
            KeysBackupState.BackingUp                  -> {
                genericItem {
                    id("summary")
                    title(stringProvider.getString(R.string.keys_backup_settings_status_ok))
                    style(GenericItem.STYLE.BIG_TEXT)
                    hasIndeterminateProcess(true)

                    val totalKeys = session.cryptoService().inboundGroupSessionsCount(false)
                    val backedUpKeys = session.cryptoService().inboundGroupSessionsCount(true)

                    val remainingKeysToBackup = totalKeys - backedUpKeys

                    if (data.keysBackupVersionTrust()?.usable == false) {
                        description(stringProvider.getString(R.string.keys_backup_settings_untrusted_backup))
                    } else {
                        description(stringProvider.getQuantityString(R.plurals.keys_backup_info_keys_backing_up, remainingKeysToBackup, remainingKeysToBackup))
                    }
                }

                isBackupAlreadySetup = true
            }
        }

        if (isBackupAlreadySetup) {
            // Add infos
            genericItem {
                id("version")
                title(stringProvider.getString(R.string.keys_backup_info_title_version))
                description(keyVersionResult?.version ?: "")
            }

            genericItem {
                id("algorithm")
                title(stringProvider.getString(R.string.keys_backup_info_title_algorithm))
                description(keyVersionResult?.algorithm ?: "")
            }

            if (vectorPreferences.developerMode()) {
                buildKeysBackupTrust(data.keysBackupVersionTrust)
            }
        }

        // Footer
        keysBackupSettingFooterItem {
            id("footer")

            if (isBackupAlreadySetup) {
                textButton1(stringProvider.getString(R.string.keys_backup_settings_restore_backup_button))
                clickOnButton1(View.OnClickListener { listener?.didSelectRestoreMessageRecovery() })

                textButton2(stringProvider.getString(R.string.keys_backup_settings_delete_backup_button))
                clickOnButton2(View.OnClickListener { listener?.didSelectDeleteSetupMessageRecovery() })
            } else {
                textButton1(stringProvider.getString(R.string.keys_backup_setup))
                clickOnButton1(View.OnClickListener { listener?.didSelectSetupMessageRecovery() })
            }
        }
    }

    private fun buildKeysBackupTrust(keysVersionTrust: Async<KeysBackupVersionTrust>) {
        when (keysVersionTrust) {
            is Uninitialized -> Unit
            is Loading       -> {
                loadingItem {
                    id("trust")
                }
            }
            is Success       -> {
                keysVersionTrust().signatures.forEach {
                    genericItem {
                        id(UUID.randomUUID().toString())
                        title(stringProvider.getString(R.string.keys_backup_info_title_signature))

                        val isDeviceKnown = it.device != null
                        val isDeviceVerified = it.device?.isVerified ?: false
                        val isSignatureValid = it.valid
                        val deviceId: String = it.deviceId ?: ""

                        if (!isDeviceKnown) {
                            description(stringProvider.getString(R.string.keys_backup_settings_signature_from_unknown_device, deviceId))
                            endIconResourceId(R.drawable.e2e_warning)
                        } else {
                            if (isSignatureValid) {
                                if (session.sessionParams.deviceId == it.deviceId) {
                                    description(stringProvider.getString(R.string.keys_backup_settings_valid_signature_from_this_device))
                                    endIconResourceId(R.drawable.e2e_verified)
                                } else {
                                    if (isDeviceVerified) {
                                        description(stringProvider.getString(R.string.keys_backup_settings_valid_signature_from_verified_device, deviceId))
                                        endIconResourceId(R.drawable.e2e_verified)
                                    } else {
                                        description(stringProvider.getString(R.string.keys_backup_settings_valid_signature_from_unverified_device, deviceId))
                                        endIconResourceId(R.drawable.e2e_warning)
                                    }
                                }
                            } else {
                                // Invalid signature
                                endIconResourceId(R.drawable.e2e_warning)
                                if (isDeviceVerified) {
                                    description(stringProvider.getString(R.string.keys_backup_settings_invalid_signature_from_verified_device, deviceId))
                                } else {
                                    description(stringProvider.getString(R.string.keys_backup_settings_invalid_signature_from_unverified_device, deviceId))
                                }
                            }
                        }
                    }
                } // end for each
            }
            is Fail          -> {
                errorWithRetryItem {
                    id("trust")
                    text(stringProvider.getString(R.string.keys_backup_unable_to_get_trust_info))
                    listener { listener?.loadTrustData() }
                }
            }
        }
    }

    interface Listener {
        fun didSelectSetupMessageRecovery()
        fun didSelectRestoreMessageRecovery()
        fun didSelectDeleteSetupMessageRecovery()
        fun loadTrustData()
        fun loadKeysBackupState()
    }
}
